package com.jackrain.nea.core.utility;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.jackrain.nea.config.Resources;
import com.jackrain.nea.core.db.Tools;
import com.jackrain.nea.core.mapper.MapperShortcut;
import com.jackrain.nea.core.schema.Table;
import com.jackrain.nea.core.schema.TableManager;
import com.jackrain.nea.exception.NDSException;
import com.jackrain.nea.util.ValueHolder;
import com.jackrain.nea.web.face.User;
import com.jackrain.nea.web.query.DefaultWebEvent;
import com.jackrain.nea.web.query.QuerySession;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

@Slf4j
public class CommandData {
    @Getter
    private final long objId;
    @Getter
    private final User user;
    private final int action; // 1新增 2修改 3删除主表 4不删除主表
    @Getter
    private JSONObject param;
    private QuerySession session;
    private JSONObject data;
    @Getter
    private DefaultWebEvent event;
    private ValueHolder vh;
    private JSONObject fixcolumn;
    private JSONObject tabitem;

    @Setter
    private boolean noOrgId = false;

    public CommandData(QuerySession session) {
        this.session = session;
        user = session.getUser();
        this.event = session.getEvent();
        Object obj = event.getParameterValue("param");
        if (obj == null) {
            throw exception("参数错误");
        }

        this.param = JSON.parseObject(JSON.toJSONStringWithDateFormat(obj, "yyyy-MM-dd HH:mm:ss", SerializerFeature.WriteMapNullValue), Feature.OrderedField);

        this.objId = param.getLong("objid");

        if (param.containsKey("isdelmtable")) { //表示删除逻辑
            if (objId <= 0) { // 此时必须有 id 值
                throw exception("未执行操作：", "无效的ID值");
            }
            this.action = param.getBooleanValue("isdelmtable") ? 3 : 4;
            if (param.containsKey("tabitem")) {
                this.tabitem = param.getJSONObject("tabitem");
            }
        } else if (param.containsKey("fixcolumn")) { //存在 fixcolumn 表示 新增或修改逻辑
            this.fixcolumn = param.getJSONObject("fixcolumn");
            this.action = this.objId < 0 ? 1 : 2;
        } else {
            throw notSupported();
        }

        this.vh = new ValueHolder();
    }

    public boolean isInserting() {
        return this.action == 1;
    }

    public boolean isDeleting() {
        return this.action == 3 || this.action == 4;
    }

    public boolean isModifying() {
        return this.action == 2;
    }

    public boolean isDeleteMainTable() {
        return this.action == 3;
    }

    public JSONObject getMainData(String tableName, boolean throwIfNotFound) {
        tableName = tableName.toUpperCase();
        if (this.fixcolumn != null && this.fixcolumn.containsKey(tableName)) {
            return keyToUpper(fixcolumn.getJSONObject(tableName));
        }
        return new JSONObject();
    }


    public JSONArray getItemData(String tableName, boolean throwIfNotFound) {
        tableName = tableName.toUpperCase();
        if (this.fixcolumn != null && this.fixcolumn.containsKey(tableName)) {
            return keyToUpper(fixcolumn.getJSONArray(tableName));
        }
        return new JSONArray();
    }

    public JSONObject getItemSingle(String tableName, boolean throwIfNotFound) {
        tableName = tableName.toUpperCase();
        if (this.fixcolumn != null && this.fixcolumn.containsKey(tableName)) {
            return keyToUpper(fixcolumn.getJSONObject(tableName));
        }
        return new JSONObject();
    }

    public long[] getItemsDeleteIds(String tableName) {
        tableName = tableName.toUpperCase();
        if (this.tabitem != null && this.tabitem.containsKey(tableName)) {
            return this.tabitem.getObject(tableName, long[].class);
        }
        return null;
    }

    public void code(int value) {
        vh.put("code", value);
    }

    public void message(String... values) {
        if (values == null || values.length == 0) {
            return;
        }
        if (values.length == 1) {
            vh.put("message", Resources.getMessage(values[0], session.getLocale()));
            return;
        }
        StringBuffer buffer = new StringBuffer();
        for (String value : values) {
            buffer.append(Resources.getMessage(value, session.getLocale()));
        }
        vh.put("message", buffer.toString());
    }

    public NDSException exception(String... messages) {
        if (messages == null || messages.length == 0) {
            return new NDSException();
        }
        if (messages.length == 1) {
            return new NDSException(Resources.getMessage(messages[0], session.getLocale()));
        }
        StringBuffer buffer = new StringBuffer();
        for (String message : messages) {
            buffer.append(Resources.getMessage(message, session.getLocale()));
        }
        return new NDSException(buffer.toString());
    }

    public NDSException notSupported() {
        return exception("不支持的操作");
    }

    public ValueHolder error(int code, String... values) {
        code(code);
        message(values);
        return result();
    }

    public ValueHolder success(String... values) {
        code(0);
        if (values == null || values.length == 0) {
            message("成功");
        } else {
            message(values);
        }
        return result();
    }

    public ValueHolder success(long objId, String tableName, String... values) {
        code(0);
        if (values == null || values.length == 0) {
            message("成功");
        } else {
            message(values);
        }
        putDataValue("objid", objId);
        putDataValue("tablename", tableName);
        return result();
    }

    public void putData(Object value) {
        this.vh.put("data", value);
        this.data = null;
    }

    public void putDataValue(String name, Object value) {
        if (this.data == null) {
            this.data = new JSONObject();
        }
        this.data.put(name, value);
    }


    public ValueHolder result() {
        if (this.errors != null) {
            code(-1);
            if (isInserting()) { //insert操作, 根据文档, 把error 放进data里
                putDataValue("error", this.errors);
            } else {
                putData(this.errors);
            }
        }
        if (this.data != null) {
            putData(this.data);
        }
        return this.vh;
    }

    public boolean checkVirtual(String name) {
        char[] cs = name.toCharArray();
        for (int i = 0; i < cs.length; i++) {
            if ((cs[i] >= '0' && cs[i] <= '9') || cs[i] == '_' || cs[i] == '-' || (cs[i] >= 'a' && cs[i] <= 'z') || (cs[i] >= 'A' && cs[i] <= 'Z')) {
                continue;
            } else {
                return true;
            }
        }
        return false;
    }

    public String convertmysqltype(String coltype) {
        int pos = coltype.indexOf("(");
        int posend = coltype.indexOf(")");
        String ptype = null;
        if (pos > 0) {
            ptype = coltype.substring(0, pos).toLowerCase();
        } else {
            ptype = coltype.toLowerCase();
        }
        int iscomm = (coltype.substring(pos + 1, coltype.length() - 1)).indexOf(",");
        if (ptype.equalsIgnoreCase("number") && iscomm > 0) {
            return "decimal" + coltype.substring(pos, coltype.length());
        }
        if (ptype.equalsIgnoreCase("number")) {
            return "bigint" + coltype.substring(pos, coltype.length());
        }
        if (ptype.equalsIgnoreCase("clob")) {
            return "longtext";
        }
        if (ptype.equalsIgnoreCase("date")) {
            return "datetime";
        }
        if (ptype.equalsIgnoreCase("varchar2")) {
            return "varchar" + coltype.substring(pos, coltype.length());
        }
        return coltype;
    }

    private final static String[] dbtypelist =
            ("date,varchar2,varchar,number,char,timestamp," +
                    "clob,blob,int,integer,decimal,numeric," +
                    "real,long,nchar,nvarchar2,boolean,smallint," +
                    "datetime,datenumber,bigint,longtext,text,time")
            .split(",");

    public void checkType(String coltype) throws NDSException {
        int pos = coltype.indexOf("(");
        int posend = coltype.indexOf(")");
        String ptype = null;
        if (pos > 0) {
            ptype = coltype.substring(0, pos).toLowerCase();
        } else {
            ptype = coltype.toLowerCase();
        }
        if (!isIn(ptype, dbtypelist)) {
            throw exception("@invalid-column-type@:", coltype);
        }
        if (pos > 0) {
            if ((posend < pos) || (posend != coltype.length() - 1)) {
                throw exception("@invalid-column-type@:", coltype);
            }

            if (!isNumeric(coltype.substring(pos + 1, coltype.length() - 1))) {
                throw exception("@invalid-column-type@:", coltype);
            }
        }
    }

    /**
     * JAVA判断字符串数组中是否包含某字符串元素
     *
     * @param substring 某字符串
     * @param source    源字符串数组
     * @return 包含则返回true，否则返回false
     */
    public static boolean isIn(String substring, String[] source) {
        if (source == null || source.length == 0) {
            return false;
        }
        for (int i = 0; i < source.length; i++) {
            String aSource = source[i].trim();
            if (aSource.equalsIgnoreCase(substring)) {
                return true;
            }
        }
        return false;
    }

    public static boolean isNumeric(String str) {
        Pattern pattern = Pattern.compile("[0-9-,]+$");
        return pattern.matcher(str).matches();
    }

    public long saveMain(MapperShortcut mapperShortcut, String tableName, String uniqueColumn) {
        if (this.isInserting()) {
            JSONObject model = this.getMainData(tableName, true);
            long id = Tools.getSequence(tableName);
            model.put("ID", id);
            setInsertDefaultValue(model);
            if (uniqueColumn != null) {
                Object uniqueValue = model.get(uniqueColumn);
                if (uniqueValue == null || ((uniqueValue instanceof String) && ((String) uniqueValue).length() == 0)) {
                    error(-1, tableName, ".", uniqueColumn, "不能为空");
                    return -1;
                }
                if (mapperShortcut.exists(tableName, uniqueColumn, uniqueValue)) {
                    error(-1, tableName, ".", uniqueColumn, "已经存在");
                    return -1;
                }
            }
            mapperShortcut.insert(model, tableName);

            putDataValue("objid", id);
            putDataValue("tablename", tableName);
            code(0);
            return id;
        } else if (this.isModifying()) {
            JSONObject model = this.getMainData(tableName, false);
            long id = this.getObjId();
            model.put("ID", id);
            setUpdateDefaultValue(model);
            if (uniqueColumn != null) {
                Object uniqueValue = model.get(uniqueColumn);
                if (uniqueValue != null && mapperShortcut.existsWithoutId(tableName, id, uniqueColumn, uniqueValue)) {
                    error(-1, tableName, ".", uniqueColumn, "已经存在");
                    return -1;
                }
            }
            mapperShortcut.update(model, tableName);
            code(0);
            return id;
        } else {
            throw this.notSupported();
        }
    }

    public boolean saveItem(MapperShortcut mapperShortcut, String tableName, JSONObject item) {
        if (item == null || !item.containsKey("ID")) {
            return false;
        }
        long id = item.getLong("ID");
        try {
            if (id == -1) {
                id = Tools.getSequence(tableName);
                item.put("ID", id);
                setInsertDefaultValue(item);
                mapperShortcut.insert(item, tableName);
                return true;
            } else {
                setUpdateDefaultValue(item);
                mapperShortcut.update(item, tableName);
                return true;
            }
        } catch (Exception ex) {
            code(-1);
            message("部分子数据写入失败");
            return false;
        }
    }

    private List<Object> errors;

    public void subError(long id, String... values) {
        if (errors == null) {
            errors = new ArrayList<>();
        }
        String message;
        if (values == null || values.length == 0) {
            message = "";
        } else if (values.length == 1) {
            message = Resources.getMessage(values[0], session.getLocale());
        } else {
            StringBuffer buffer = new StringBuffer();
            for (String value : values) {
                buffer.append(Resources.getMessage(value, session.getLocale()));
            }
            message = buffer.toString();
        }
        errors.add(new JSONObject() {{
            put("id", id);
            put("code", -1);
            put("message", message);
        }});
    }

    public void setUpdateDefaultValue(JSONObject model) {
        setUpdateDefaultValue(model, user);
    }

    public void setInsertDefaultValue(JSONObject model) {
        setInsertDefaultValue(model, user, noOrgId);
    }

    public static void setUpdateDefaultValue(JSONObject model, User user) {
        if (model != null) {
            model.put("MODIFIEDDATE", new Timestamp(System.currentTimeMillis()));
            model.put("MODIFIERID", user.getId());
            model.put("MODIFIERNAME", user.getName());
            model.put("MODIFIERENAME", user.getEname());
        }
    }


    public static void setInsertDefaultValue(JSONObject model, User user, boolean noOrgId) {
        if (model != null) {
            Timestamp timestamp = new Timestamp(System.currentTimeMillis());
            model.put("OWNERID", user.getId());
            model.put("MODIFIERID", user.getId());
            model.put("OWNERNAME", user.getName());
            model.put("MODIFIERNAME", user.getName());
            model.put("OWNERENAME", user.getEname());
            model.put("MODIFIERENAME", user.getEname());
//            model.put("STATUSERNAME",user.getName());
            model.put("CREATIONDATE", timestamp);
            model.put("MODIFIEDDATE", timestamp);
            model.put("AD_CLIENT_ID", Integer.valueOf(user.getClientId()).longValue());
            if (!noOrgId) {
                model.put("AD_ORG_ID", Integer.valueOf(user.getOrgId()).longValue());
            }
            model.put("MODIFIEDDATE", timestamp);
            model.put("CREATIONDATE", timestamp);
        }
    }

    public static JSONObject keyToUpper(JSONObject model) {
        JSONObject newModel = new JSONObject();
        for (String key : model.keySet()) {
            newModel.put(key.toUpperCase(), model.get(key));
        }
        return newModel;
    }

    public static JSONArray keyToUpper(JSONArray array) {
        for (int i = 0; i < array.size(); i++) {
            array.set(i, keyToUpper((JSONObject) array.get(i)));
        }
        return array;
    }
}
